/*
 *  Class name: ContactManagerImpl.java
 *  Version: 1.0
 *  Date: 4.6.2014
 */
package cz.muni.fi.pv168.addressbook.daos.impl;

import cz.muni.fi.pv168.addressbook.daos.ContactManager;
import cz.muni.fi.pv168.addressbook.entities.Contact;
import cz.muni.fi.pv168.addressbook.entities.validators.ContactValidator;
import cz.muni.fi.pv168.addressbook.entities.validators.Validator;
import cz.muni.fi.pv168.addressbook.entities.xml.IDGenerator;
import cz.muni.fi.pv168.addressbook.entities.xml.XMLReaderWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBException;

/**
 *
 * @author Michal Ďuriš
 */
public class ContactManagerImpl implements ContactManager {

    private List<Contact> contacts;

    private ContactValidator validator;

    private XMLReaderWriter<Contact> contactXMLReaderWriter;

    private IDGenerator idGenerator;

    private ContactManagerImpl() {
        contacts = new ArrayList<>();
    }

    private void init() throws JAXBException, IOException {

        validator = new ContactValidator();

        contactXMLReaderWriter = new XMLReaderWriter<>("contacts.xml");
        contactXMLReaderWriter.init();

        getAllContacts();

        idGenerator = new IDGenerator();
        idGenerator.setInitialValue(contacts, Contact::getId);

    }

    private void checkContactList() throws RuntimeException {

        if (!Validator.NOT_EMPTY_LIST_PREDICATE.test(contacts)) {
            throw new RuntimeException();
        }
    }

    public static ContactManager getInstance() throws Exception {

        final ContactManagerImpl manager = new ContactManagerImpl();
        manager.init();

        return manager;
    }

    @Override
    public void addContactToPerson(Contact contact) {

        if (contacts == null) {
            throw new RuntimeException();
        }

        validator.validate(contact, Validator.Test.REQUIRED);

        if (!Validator.CORRECT_ID_PREDICATE.test(contact.getId())) {

            contact.setId(idGenerator.getUniqueID());
        }

        contacts.add(contact);

        try {
            contactXMLReaderWriter.store(contacts);

        } catch (JAXBException | IOException ex) {

            contacts.remove(contact);
            throw new RuntimeException();
        }
    }

    @Override
    public void updateContact(Contact contact) {

        checkContactList();

        validator.validate(contact, Validator.Test.ID, Validator.Test.REQUIRED);

        int previousContactIndex = 0;
        Contact previousContact = new Contact();

        for (int i = 0; i < contacts.size(); i++) {

            Contact currentContact = contacts.get(i);

            if (currentContact.getId().equals(contact.getId())) {

                previousContactIndex = i;
                previousContact.setContact(currentContact);

                currentContact.setContact(contact);
            }
        }

        try {

            contactXMLReaderWriter.store(contacts);

        } catch (JAXBException | IOException ex) {

            contacts.get(previousContactIndex).setContact(previousContact);

            throw new RuntimeException();
        }

    }

    @Override
    public void removeContactFromPerson(Contact contact) {

        checkContactList();

        validator.validate(contact, Validator.Test.ID, Validator.Test.REQUIRED);

        contacts.remove(contact);

        try {

            contactXMLReaderWriter.store(contacts);

        } catch (JAXBException | IOException ex) {

            contacts.add(contact);

            throw new RuntimeException();
        }
    }

    @Override
    public void removeContactsFromPerson(List<Contact> contacts) {

        List<Contact> alreadyRemovedContacts = new ArrayList<>(contacts.size());

        try {

            contacts.stream().forEach((contact) -> {
                removeContactFromPerson(contact);
                alreadyRemovedContacts.add(contact);
            });

        } catch (RuntimeException removeEx) {

            this.contacts.addAll(alreadyRemovedContacts);

            try {

                contactXMLReaderWriter.store(this.contacts);

            } catch (JAXBException | IOException rollbackEx) {

                throw new RuntimeException(rollbackEx);
            }

            throw new RuntimeException(removeEx);
        }

    }

    @Override
    public List<Contact> getAllContacts() {

        try {
            contacts = contactXMLReaderWriter.load();
        } catch (JAXBException ex) {

            throw new RuntimeException(ex);
        }

        return contacts;
    }
}
